Node.js web编程

HTTP是Node里的一等公民。实际上,node以web服务器起步,并且在很多的框架中大放光彩。

基本的流式HTTP服务器

Node的HTTP模块用来处理流和低延迟。Node也是现在流行的用来创建和运行服务器的工具(还有python)。

引入http模块,使用createServer方法来创建服务器,它是一个事件触发对象,有许多事件。在用res写入数据的时候,使用end可以关闭流,如果用write流将持续打开,如果服务器长时间不返回数据,那么在默认的超时时间内,将会关闭连接。自定义超时时间使用server.timeout。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const server = require('http').createServer();
server.on('request', (req, res) => {
res.writeHead(200, {'content-type': 'text/plain'});
setTimeout(() => {
res.write('Ain\'t gonna happen\n');
},10000);
// 使用end可以终止res流
res.end('Hello world\n');
});
server.timeout = 1000;
server.listen(8000);

使用HTTPS

HTTPS是TLS/SSL之上的HTTP,Node有许多模块来处理HTTPS,但是和HTTP模块很类似。只需要把http模块换成https模块,createServer中传入一个对象,对象中是一个key和certificate。可以是buffer或者string,代表独立的key和certificate。也可以传入pfx来组合它们。

首先,需要生成一个证书,我们可以使用openssl来生成一个私钥,并且允许我们生成一个签名证书请求(CSR),自己签署来做测试。当然,浏览器不会信任自签名证书,但是对于测试目的来说可以了。

1
$ openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -nodesx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const fs = require('fs');
const server = require('https')
.createServer({
key: fs.readFileSync('./key.pem'),
cert: fs.readFileSync('./cert.pem')
});
server.on('request', (req, res) => {
res.writeHead(200, {});
res.end('Hello World\n');
});
// https default port and we can run this script with sudo.
server.listen(443);

请求HTTP/HTTPS数据

Node可以被当作客户端来请求http和https数据。Node的HTTP模块中主要有五个主要的类。

  • http.Server:创建基本的服务器,继承自net.Server,是一个事件触发器
  • http.Agent:用来管理被用于HTTP客户端请求的pooling sockets。Node默认使用全局agent,我们也可以自定义创建符合需求的
  • http.IncomingMessage:实现了可读流接口,请求对象
  • http.ServerResponse:实现了可写流接口,由HTTP服务器内部产生
  • http.ClientRequest:实现了可写流接口,当初始化一个HTTP请求的时候,我们其实在处理ClientRequest对象,这个和我们之前看到的服务器例子里面的请求对象不同,例子中的请求对象是IncomingMessage对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// clent
const http = require('http');
// req: http.ClientRequest
const req = http.get('http://www.google.com', res => {
// res: http:IncomingMessage
console.log(res.statusCode);
console.log(res.headers);
res.on('data', data => {
console.log(data.toString());
});
});
req.on('error', e => {
console.log(e);
});
// 管理请求的sockets
console.log(req.agent); // http.Agent
//=======================================//
// server: http.Server
const server = require('http').createServer();
server.on('request', (req, res) => {
// req: http.IncomingMessage
// res: http.ServerResponse
res.writeHead(200, {'content-type': 'text/plain'});
res.end('Hello World\n');
});
server.listen(8000);

路由

用http模块来处理路由,实际上就是根据请求的URL来返回不同的结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const server = require('http').createServer();
const fs = require('fs');
const data = {};
server.on('request', (req, res) => {
switch(req.url) {
case '/api':
res.writeHead(200, {'content-type': 'application/json'});
res.end(JSON.stringify(data));
case '/home':
case '/about':
res.writeHead(200, {'content-type': 'text/plain'});
res.end(fs.readFileSync(`${req.url}.html`)); // 同级目录下有和req.url一样的html文件
break;
case '/':
res.writeHead(301, {'location': '/home'});
res.end();
break;
default:
res.writeHead(404);
res.end();
}
});
server.listen(8000);

解析URLs和查询字符串

主要用url/querystring模块来进行一些操作,这部分比较简单,就不列出了。
URL组成